package com.zoe.onelink.core.web.resolver;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zoe.onelink.annotation.web.arg.RequestArg;
import com.zoe.onelink.common.enumeration.ExceptionCodeEnum;
import com.zoe.onelink.common.exception.ApiException;
import com.zoe.onelink.core.property.OnelinkProperties;
import com.zoe.onelink.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;

import java.util.Collection;

/**
 * <p>标题: 请求参数解析器</p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-11-01
 */
@Slf4j
public class ObjectMethodArgumentResolver extends AbstractObjectMapperMethodArgumentResolver {

    private static final String EMPTY_QUOTE = "\"\"";
    private static final String NULL_STR_VALUE = "null";


    public ObjectMethodArgumentResolver(ObjectMapper objectMapper, OnelinkProperties.Web.Arg requestArgProp) {
        super(objectMapper, requestArgProp);
    }

    @Override
    protected boolean couldResolveArgument(MethodParameter parameter) {
        if (this.requestArgProp.getAnnotationStrategy() == OnelinkProperties.Web.Arg.AnnotationStrategy.ONLY_ANNOTATED) {
            return parameter.hasParameterAnnotation(RequestArg.class) || parameter.hasMethodAnnotation(RequestArg.class);
        }
        return true;
    }

    @Override
    protected Object resolveArgument(JsonNode rootNode, JavaType paramType, RequestArg requestArg, String paramName, int paramIndex) throws Exception {
        final Object val;
        // 注解在方法上而不是在参数上时
        if (requestArg == null) {
            JsonNode valNode = rootNode.get(paramName);
            val = valNode == null || (paramType.isPrimitive() && EMPTY_QUOTE.equals(valNode.toString()))
                    ? null : om.readValue(valNode.toString(), paramType);
            // 嵌套对象
        } else if (requestArg.nested()) {
            JsonNode valNode = rootNode.path(paramName);
            String nodeStr = valNode.toString();
            boolean isEmptyNode = StrUtil.isBlank(nodeStr) || EMPTY_QUOTE.equalsIgnoreCase(nodeStr);
            // 基本类型且入参值为空的情况
            if (paramType.isPrimitive() && isEmptyNode) {
                val = StrUtil.isBlank(requestArg.defaultValue()) ? null : om.readValue(requestArg.defaultValue(), paramType);
                // 字符串类型处理
            } else if (paramType.getRawClass() == String.class) {
                // 字段不存在
                if (valNode.isMissingNode() || NULL_STR_VALUE.equals(nodeStr)) {
                    val = StrUtil.emptyToNull(requestArg.defaultValue());
                    // 空值字段
                } else if (EMPTY_QUOTE.equals(nodeStr)) {
                    val = StrUtil.emptyToDefault(requestArg.defaultValue(), "");
                } else {
                    val = om.readValue(nodeStr, paramType);
                }
                // 不是实体类型并且入参值为空
            } else if (!this.isGenericType(paramType.getRawClass()) && isEmptyNode) {
                val = null;
            } else {
                val = om.readValue(nodeStr, paramType);
            }
        } else {
            // 非嵌套对象,使用根节点解析
            val = om.readValue(rootNode.toString(), paramType);
        }
        final Object retVal;
        // 开启了自动填充,并且入参类型为实体类
        if (val == null && this.couldAutoFill(rootNode, paramType)) {
            retVal = om.readValue(rootNode.toString(), paramType);
        } else {
            retVal = val;
        }
        if (paramType.isPrimitive() && (retVal == null || StrUtil.isEmpty(retVal.toString()))) {
            throw ApiException.createEx(ExceptionCodeEnum.REQ_ARG_NOT_EMPTY, paramIndex, paramName);
        }
        return retVal;
    }

    /**
     * 是否可以自动填充
     */
    protected boolean couldAutoFill(JsonNode rootNode, JavaType paramType) {
        // 已开启自动填充并且类型是普通类型
        boolean basicPermit = this.requestArgProp.isAutoFillNested() && !this.isGenericType(paramType.getRawClass());
        // 数组或集合类型
        boolean arrayPermit = rootNode.isArray() == (paramType.isArrayType() || Collection.class.isAssignableFrom(paramType.getRawClass()));
        return basicPermit && arrayPermit;
    }

}
